package org.feichao.xdao;

import org.apache.commons.lang.StringUtils;
import org.feichao.xdao.annotations.DAO;
import org.feichao.xdao.annotations.SQL;
import org.feichao.xdao.annotations.SQLParam;
import org.feichao.xdao.util.Log;
import org.feichao.xdao.util.SQLContext;
import org.feichao.xdao.util.SQLRunner;

import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;

/**
 * DAO的实现
 *
 * @author chao
 * @version 2015-05-25
 */
public class DAOImplement implements InvocationHandler {

	@Override
	public Object invoke(Object self, Method method, Object[] args) throws Throwable {
		try{
			if(method.isAnnotationPresent(SQL.class)){ //标记@SQL
				return sqlQuery(self, method, args);
            }else{//没有标记@SQL
                Log.warn("Please annotation the method[%s], such as @SQL", method.getName());
            }
		}catch(Throwable e){
			Log.error(e, "Failed to invoke %s", method.getName());
		}
		return null;
	}

	private Object sqlQuery(Object self, Method method, Object[] args) throws Exception {
		SQL sqlAnn = method.getAnnotation(SQL.class);
		String sqlTemplate = sqlAnn.value();
		SQLContext context = new SQLContext();
		context.sqlTemplate = StringUtils.trimToEmpty(sqlTemplate);
        //处理dao里面的方法参数，将参数放入context.params，如果标记了SQLParam则会改变这个参数的名称
        if(args != null){
            for (int i = 0; i < args.length; i++) {
                Object arg = args[i];
                Annotation[] annotations = method.getParameterAnnotations()[i];
                if(annotations.length == 0){
                    context.params.put(String.valueOf(i+1), arg);
                }else{
                    for(Annotation a: annotations){
                        if(a.annotationType().equals(SQLParam.class)){
                            SQLParam sqlParamAnn= (SQLParam) a;
                            context.params.put(sqlParamAnn.value(), arg);
                        }
                    }
                }
            }
        }

		SQLRunner runner = new SQLRunner(context);
        //处理返回类型，以及对于List类型的泛型处理
        Class returnType = method.getReturnType(); //返回类型
        Class actualReturnType = returnType; //返回类型的泛型，默认和返回类型一样
        if(method.getReturnType().isAssignableFrom(List.class)){
            Type fc = method.getGenericReturnType(); // 关键的地方，如果是List类型，得到其Generic的类型

            if(fc == null){
                Log.warn("DAO method must specific the generic return type if you want to get a list!");
                return null;
            }else{
                if(fc instanceof ParameterizedType){//如果是泛型参数的类型
                    ParameterizedType pt = (ParameterizedType) fc;
                    Class genericClazz = (Class)pt.getActualTypeArguments()[0]; //得到泛型里的class类型对象
                    actualReturnType = genericClazz;
                }else{
                    Log.warn("DAO method must specific the generic return type if you want to get a list!");
                    return null;
                }
            }
        }
        Object resultData = null;
        try{
            resultData = runner.execute(returnType, actualReturnType);
        }catch (Exception e){
            //查找DAO接口，打印错误日志，便于调试定位
            Class daoClass = null;
            for(Class inter: self.getClass().getInterfaces()){
                if(inter.isAnnotationPresent(DAO.class)){
                    daoClass = inter;
                    break;
                }
            }
            String daoClassName = "UnknownDAO";
            if(daoClass != null){
                daoClassName = daoClass.getCanonicalName();
            }
            Log.error(e, "Error in executing DAO method[%s.%s]", daoClassName, method.getName());
        }
		return resultData;
	}
}
